package prefuse.data.io;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;

import prefuse.util.io.IOLib;

/**
 * Helper class that stores character length counts for each column in
 * a fixed width text table. This class is needed for reading and writing
 * fixed width data tables. A schema definition can either be created
 * manually using the {@link #addColumn(String, int)} method or loaded from
 * a tab-delimited text file using the {@link #load(String)} method.
 * 
 * @author <a href="http://jheer.org">jeffrey heer</a>
 */
public class FixedWidthTextTableSchema {

    private String[] names = new String[0];
    private int[] cols = new int[1];

    /**
     * Creates a new, initially empty FixedWidthTextTableSchema.
     */
    public FixedWidthTextTableSchema() {

    }

    private void ensureCapacity(int cap) {
        String[] nnames = new String[names.length + 1];
        System.arraycopy(names, 0, nnames, 0, names.length);
        names = nnames;

        int[] ncols = new int[cols.length + 1];
        System.arraycopy(cols, 0, ncols, 0, cols.length);
        cols = ncols;
    }

    /**
     * Adds a column to this schema description.
     * @param name the name of this column
     * @param length the length, in text characters, of this column in a data file
     */
    public void addColumn(String name, int length) {
        int idx = names.length;
        ensureCapacity(idx + 1);
        names[idx] = name;
        cols[idx + 1] = cols[idx] + length;
    }

    /**
     * Returns the number of columns in this schema.
     * @return the numner of columns
     */
    public int getColumnCount() {
        return names.length;
    }

    /**
     * Gets the name of the requested column
     * @param idx the index of the column
     * @return the name of the column
     */
    public String getColumnName(int idx) {
        return names[idx];
    }

    /**
     * Gets the character length of the given column
     * @param idx the index of the column
     * @return the character length of the column in the fixed-width format
     */
    public int getColumnLength(int idx) {
        return cols[idx + 1] - cols[idx];
    }

    /**
     * Gets the starting character number for the given column index
     * @param idx the index of the column
     * @return the text character position at which this column starts on a
     * line
     */
    public int getColumnStart(int idx) {
        return cols[idx];
    }

    /**
     * Sets the ending character number for the given column index. This value
     * is one greater than the last character position for the column.
     * @param idx the index of the column
     * @return one greater than the last text character position at which this
     * column ends on a line
     */
    public int getColumnEnd(int idx) {
        return cols[idx + 1];
    }

    /**
     * Writes this schema description to a file with the given name.
     * @param filename the name of the file
     * @throws DataIOException if an IO exception occurs
     */
    public void write(String filename) throws DataIOException {
        try {
            write(new FileOutputStream(filename));
        } catch (FileNotFoundException e) {
            throw new DataIOException(e);
        }
    }

    /**
     * Writes this schema description to the given output stream.
     * @param os the output stream
     * @throws DataIOException if an IO exception occurs
     */
    public void write(OutputStream os) throws DataIOException {
        try {
            PrintStream out = new PrintStream(new BufferedOutputStream(os));
            for (int i = 0; i < names.length; ++i) {
                out.print(names[i]);
                out.print('\t');
                out.print(cols[i + 1] - cols[i]);
                out.println();
            }
        } catch (Exception e) {
            throw new DataIOException(e);
        }
    }

    /**
     * Loads a schema description from the given location.
     * @param loc a location string representing a filename, URL, or resource locator
     * @return the loaded schema description
     * @throws DataIOException if an IO exception occurs
     */
    public static FixedWidthTextTableSchema load(String loc) throws DataIOException {
        try {
            InputStream is = IOLib.streamFromString(loc);
            if (is == null)
                return null;
            FixedWidthTextTableSchema fws = new FixedWidthTextTableSchema();
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            String line;

            while ((line = br.readLine()) != null) {
                String[] tok = line.split("\t");
                fws.addColumn(tok[0], Integer.parseInt(tok[1]));
            }

            return fws;
        } catch (Exception e) {
            throw new DataIOException(e);
        }
    }

} // end of class FixedWidthTextTableReader